﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using ThouShaltNotBrick.Logic.Collisions;
using ThouShaltNotBrick.Logic.GeneralObjects;
using GameUtils;
using GMLogging;

namespace ThouShaltNotBrick.Logic.Helpers
{
    public sealed class LevelUpdater
    {
        #region Methods
        public UpdateReturnValue Update(
            GameTime pGameTime,
            PlayData pPlayData)
        {
            return UpdateBall(
                pGameTime,
                pPlayData.CurrentLevel.Balls[0],
                pPlayData);
        }

        private UpdateReturnValue UpdateBall(
            GameTime pGameTime,
            Ball pBall,
            PlayData pPlayData)
        {
            LevelWithCurrentState currentLevel = pPlayData.CurrentLevel;

            float elapsed = (float)pGameTime.ElapsedGameTime.TotalSeconds;
            pBall.Position += elapsed * pBall.Speed.ToVector2();

            Pad pad = currentLevel.Pad;
            float? padCollisionNormlizedOffset = CollisionHelpers.CheckPadCollision(pBall, pad);
            if (padCollisionNormlizedOffset.HasValue && !CollisionHelpers.IsPadCollision(pBall.PrevCollisionObject))
            {
                float direction = CollisionHelpers.GetPadReflectDirection(padCollisionNormlizedOffset.Value);
                pBall.Speed = new PolarVector2(pBall.Speed.Magnitude, direction);
                pBall.PrevCollisionObject = new CollisionObject(CollisionObjectType.Pad, CollisionSide.Top);
                pBall.PrevPosition = pBall.Position;
                return new UpdateReturnValue(false);
            }

            CollisionSide boundsCollisionSide = CollisionHelpers.CheckBoundsCollision(pBall);
            if (boundsCollisionSide != CollisionSide.None &&
                !CollisionHelpers.IsBoundsCollisionOnProvidedSide(pBall.PrevCollisionObject, boundsCollisionSide))
            {
                PolarVector2 ballSpeed = pBall.Speed;
                float ballSpeedX = ballSpeed.X;
                float ballSpeedY = ballSpeed.Y;
                if (((boundsCollisionSide & CollisionSide.Left) != 0 && ballSpeedX < 0.0f) ||
                    ((boundsCollisionSide & CollisionSide.Right) != 0 && ballSpeedX > 0.0f))
                {
                    ballSpeed.NegateX();
                }
                if (((boundsCollisionSide & CollisionSide.Bottom) != 0 && ballSpeedY < 0.0f) ||
                    ((boundsCollisionSide & CollisionSide.Top) != 0 && ballSpeedY > 0.0f))
                {
                    ballSpeed.NegateY();
                }
                pBall.Speed = ballSpeed;
                pBall.PrevCollisionObject = new CollisionObject(CollisionObjectType.Bounds, boundsCollisionSide);
                pBall.PrevPosition = pBall.Position;

                if ((boundsCollisionSide & CollisionSide.Bottom) != 0)
                {
                    pPlayData.ChangeLives(-1);
                    return new UpdateReturnValue(true);
                }
                else
                {
                    return new UpdateReturnValue(false);
                }
            }

            BrickWithCurrentState[,] bricks = currentLevel.Bricks;
            List<CollisionObject> possibleBrickCollisions = new List<CollisionObject>();
            for (int i = 0; i < Constants.BRICK_ROWS; i++)
            {
                for (int j = 0; j < Constants.BRICK_COLUMNS; j++)
                {
                    BrickWithCurrentState brick = bricks[i, j];
                    if (BrickHelpers.IsBrickExisting(brick) && CollisionHelpers.CanCollisionOccur(pBall, brick))
                    {
                        CollisionSide collisionSide = CollisionHelpers.CheckBrickCollision(pBall, brick, bricks);
                        if (collisionSide != CollisionSide.None)
                        {
                            CollisionObject collisionObject = new CollisionObject(CollisionObjectType.Brick, collisionSide, i, j);
                            possibleBrickCollisions.Add(collisionObject);
                        }
                    }
                }
            }

            //if (possibleBrickCollisions.Count > 0)
            //{
            //    StringBuilder sb = new StringBuilder();
            //    sb.AppendFormat("Possible brick collision count: {0}", possibleBrickCollisions.Count).AppendLine();
            //    foreach (CollisionObject collisionObject in possibleBrickCollisions)
            //    {
            //        sb.AppendFormat("Row {0}; Col {1}", collisionObject.BrickRow, collisionObject.BrickColumn).AppendLine();
            //    }
            //    Logger.Info(sb.ToString());
            //}

            CollisionObject brickCollisionObject = CollisionHelpers.ResolveCollisionObject(pBall, possibleBrickCollisions);
            if (brickCollisionObject != null)
            {
                PolarVector2 ballSpeed = pBall.Speed;
                float ballSpeedX = ballSpeed.X;
                float ballSpeedY = ballSpeed.Y;
                CollisionSide brickCollisionSide = brickCollisionObject.CollisionSide;
                BrickWithCurrentState brick = bricks[brickCollisionObject.BrickRow, brickCollisionObject.BrickColumn];
                if (((brickCollisionSide & CollisionSide.Left) != 0 && ballSpeedX > 0.0f) ||
                    ((brickCollisionSide & CollisionSide.Right) != 0 && ballSpeedX < 0.0f))
                {
                    ballSpeed.NegateX();
                }
                if (((brickCollisionSide & CollisionSide.Bottom) != 0 && ballSpeedY > 0.0f) ||
                    ((brickCollisionSide & CollisionSide.Top) != 0 && ballSpeedY < 0.0f))
                {
                    ballSpeed.NegateY();
                }
                brick.SetNextBrickState();
                pBall.Speed = ballSpeed;
                pBall.PrevCollisionObject = brickCollisionObject;
                pBall.PrevPosition = pBall.Position;
                return new UpdateReturnValue(false);
            }

            pBall.PrevPosition = pBall.Position;
            return new UpdateReturnValue(false);
        }
        #endregion
    }
}